﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace TorchView.GameContent.Data
{
  public static class DatSerializer
  {
    public static DataBlock Load(string filename)
    {
      string data = File.ReadAllText(filename);

      return Parse(data);
    }

    public static DataBlock Parse(string data)
    {
      DataBlock currentBlock = null;
      Stack<DataBlock> blocks = new Stack<DataBlock>();
      for (int i = 0; i < data.Length; i++)
      {
        char c = data[i];
        int nextPosition = i + 1;
        if (c == '[')
        {
          if (data[nextPosition] != '/')
          {
            var newDataBlock = new DataBlock();
            newDataBlock.Name = data.Substring(nextPosition, data.IndexOf(']', nextPosition) - nextPosition);
            i += newDataBlock.Name.Length + 1;
            if (currentBlock != null)
              currentBlock.SubBlocks.Add(newDataBlock);
            blocks.Push(newDataBlock);
            currentBlock = newDataBlock;
          }
          else
          {
            currentBlock = blocks.Pop();
            i += data.IndexOf(']', nextPosition) - nextPosition + 1;
          }
        }
        else if (c == '<')
        {
          string propertyType = data.Substring(nextPosition, data.IndexOf('>', nextPosition) - nextPosition);
          i += propertyType.Length + 2;
          string propertyName = data.Substring(i, data.IndexOf(':', i) - i);
          i += propertyName.Length + 1;
          string propertyValue = data.Substring(i, data.IndexOfAny(new char[] { '<', '[', '\r', '\n' }, i) - i);
          i += propertyValue.Length - 1;

          PropertyType type = GetPropertyType(propertyType);
          object value = GetValue(type, propertyValue);

          if (currentBlock != null)
            currentBlock.Properties.Add(new DataProperty { Name = propertyName, Type = type, Value = value });
          else
            throw new ApplicationException("Failed to parse file.  Unable to attach property, no block.");
        }
      }
      return currentBlock;
    }

    private static object GetValue(PropertyType type, string value)
    {
      switch (type)
      {
        case PropertyType.INTEGER:
          int int32;
          if (int.TryParse(value, out int32))
            return int32;
          break;
        case PropertyType.FLOAT:
          float floatValue;
          if (float.TryParse(value, out floatValue))
            return floatValue;
          break;
        case PropertyType.DOUBLE:
          double doubleValue;
          if (double.TryParse(value, out doubleValue))
            return doubleValue;
          break;
        case PropertyType.UINTEGER:
          uint uint32;
          if (uint.TryParse(value, out uint32))
            return uint32;
          break;
        case PropertyType.TRANSLATE:
        case PropertyType.STRING:
          return value;
        case PropertyType.BOOL:
          bool boolValue;
          if (bool.TryParse(value, out boolValue))
            return boolValue;
          if (value == "0")
            return false;
          if (value == "1")
            return true;
          break;
        case PropertyType.INTEGER64:
          long int64;
          if (long.TryParse(value, out int64))
            return int64;
          break;
        case PropertyType.UNKNOWN:
        default:
          break;
      }
      Debug.Fail("Failed to parse property value.", string.Format("Type: {0}, Value: {1}", type, value));
      return null;
    }

    private static PropertyType GetPropertyType(string propertyType)
    {
      switch (propertyType)
      {
        case "INTEGER": return PropertyType.INTEGER;
        case "FLOAT": return PropertyType.FLOAT;
        case "DOUBLE": return PropertyType.DOUBLE;
        case "UNSIGNED INT": return PropertyType.UINTEGER;
        case "STRING": return PropertyType.STRING;
        case "BOOL": return PropertyType.BOOL;
        case "INTEGER64": return PropertyType.INTEGER64;
        case "TRANSLATE": return PropertyType.TRANSLATE;
        default:
          Debug.Fail("Unknown property type. Type: " + propertyType);
          return PropertyType.UNKNOWN;
      }
    }

  }
}
